package net.qunzhi.modules.cms.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.qunzhi.common.utils.*;
import net.qunzhi.modules.cms.dao.TbCopyFlowDataDao;
import net.qunzhi.modules.cms.dao.TbEquipmentDao;
import net.qunzhi.modules.cms.dao.TbFlowStateDao;
import net.qunzhi.modules.cms.dao.TbPressureStateDao;
import net.qunzhi.modules.cms.entity.TbCopyFlowDataEntity;
import net.qunzhi.modules.cms.entity.TbEquipmentEntity;
import net.qunzhi.modules.cms.entity.TbFlowStateEntity;
import net.qunzhi.modules.cms.query.CompletionData;
import net.qunzhi.modules.cms.query.DailyRecordListQuery;
import net.qunzhi.modules.cms.query.DateInfoQuery;
import net.qunzhi.modules.cms.service.*;
import net.qunzhi.modules.cms.utils.DiaoduUtils;
import net.qunzhi.modules.cms.vo.*;
import net.qunzhi.modules.historyspider.ElectricitySpiderCrawler;
import net.qunzhi.modules.historyspider.FlowSpiderCrawler;
import net.qunzhi.modules.historyspider.PressureSpiderCrawler;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.bcel.Const;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.sound.sampled.DataLine;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;


@Service("tbEquipmentService")
public class TbEquipmentServiceImpl extends ServiceImpl<TbEquipmentDao, TbEquipmentEntity> implements TbEquipmentService {
    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private TbFlowStateService tbFlowStateService;
    @Autowired
    private TbPressureStateService tbPressureStateService;
    @Autowired
    private TbFlowStateDao tbFlowStateDao;
    @Autowired
    private TbPressureStateDao tbPressureStateDao;
    @Autowired
    private DiaoduUtils diaoduUtils;
    @Autowired
    private TbCopyFlowDataDao copyFlowDataDao;
    @Autowired
    private DzFlowDataService dzFlowDataService;
    @Autowired
    private XjFlowDataService xjFlowDataService;
    @Autowired
    private TbCopyFlowDataService copyFlowDataService;

    @Override
    public PageUtils queryPage(BaseQuery query) {
        IPage<TbEquipmentEntity> page = this.page(
                new Query<TbEquipmentEntity>().getPage(query),
                new QueryWrapper<TbEquipmentEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public void getSpiderData() {

        List<TbEquipmentEntity> equipmentList = this
                .list(new QueryWrapper<TbEquipmentEntity>()
                        .eq("type", Constant.EquipmentType.SPIDER.getCode())
                        .eq("is_used", Constant.ONE)
                );
        equipmentList.forEach(equipment -> {
            if (StringUtils.isNotBlank(equipment.getTag())) {// 压力
                tbPressureStateService.addPressureData(equipment);
            }
            if (StringUtils.isNotBlank(equipment.getFlowTag())) { // 流量
                tbFlowStateService.addFlowData(equipment);
            }
            // 校验压力和流量是否更新到今天日期
            Integer spiderStatus = checkIsPressureSuccess(equipment.getId(), equipment.getTag())
                    && checkIsFlowSuccess(equipment.getId(), equipment.getFlowTag()) ?
                    Constant.EquipmentSpiderStatus.DONE.getCode() : Constant.EquipmentSpiderStatus.INCOMPLETE.getCode();
            this.lambdaUpdate().set(TbEquipmentEntity::getSpiderStatus, spiderStatus)
                    .eq(TbEquipmentEntity::getId, equipment.getId())
                    .update();
        });
    }

    private boolean checkIsFlowSuccess(Integer id, String flowTag) {
        if (StringUtils.isBlank(flowTag)) {
            return true;
        }
        String flowMaxTime = tbFlowStateDao.getMaxTimeByEquipmentId(id);
        String nowDate = DateUtils.format(new Date());
        return flowMaxTime.split(" ")[0].equals(nowDate);
    }

    private boolean checkIsPressureSuccess(Integer id, String tag) {
        if (StringUtils.isBlank(tag)) {
            return true;
        }
        String pressureMaxTime = tbPressureStateDao.getMaxTimeByEquipmentId(id);
        String nowDate = DateUtils.format(new Date());
        return pressureMaxTime.split(" ")[0].equals(nowDate);
    }

    @Override
    public DailyRecordListVO queryDailyRecordList(DailyRecordListQuery query) {

        setQueryData(query);
        DailyRecordListVO vo = new DailyRecordListVO();
        List<DailyRecordVO> dailyRecordVOS = baseMapper.queryDailyRecordList(query);
//        List<DailyRecordVO> dzList = dzFlowDataService.getFlowData(dailyRecordVOS.stream().filter(recordVO -> recordVO.getType().equals(Constant.EquipmentType.DZ.getCode())).collect(Collectors.toList()), query);// 装入丹灶的获取数据
//        List<DailyRecordVO> xjList = xjFlowDataService.getFlowData(dailyRecordVOS.stream().filter(recordVO -> recordVO.getType().equals(Constant.EquipmentType.XJ.getCode())).collect(Collectors.toList()), query);// 装入西江的获取数据
//        var dataList = dailyRecordVOS.stream().filter(recordVO -> recordVO.getType().equals(Constant.EquipmentType.SPIDER.getCode())).collect(Collectors.toList());// 调度中心的数据
        // 手抄的数据
//        var handDataList = dailyRecordVOS.stream().filter(recordVO -> recordVO.getType().equals(Constant.EquipmentType.HAND.getCode())).collect(Collectors.toList());
//        dataList.addAll(xjList);
//        dataList.addAll(dzList);
//        dataList.addAll(handDataList);
        vo.setRecordList(dailyRecordVOS.stream().sorted(Comparator.comparing(DailyRecordVO::getId)).collect(Collectors.toList()));
//        ArrayList<UndoCopyDataVO> undoCopyDataVOS = new ArrayList<>();// 未手抄的ids
//        setUndoDataByTime(query.getStartDateTime(), undoCopyDataVOS);
//        setUndoDataByTime(query.getEndDateTime(), undoCopyDataVOS);
//        setUndoDataByTime(query.getStartMonthTime(), undoCopyDataVOS);
//        setUndoDataByTime(query.getEndMonthTime(), undoCopyDataVOS);
//        vo.setUndoCopyDataList(undoCopyDataVOS);

        return vo;
    }

    // 设置查询参数
    public void setQueryData(DailyRecordListQuery query) {
        int day = query.getDateTime().getDayOfMonth();
        LocalDateTime dateTime = query.getDateTime().with(TemporalAdjusters.firstDayOfMonth()).withDayOfMonth(28).withHour(0).withMinute(0).withSecond(0);
        query.setDate(query.getDateTime().toLocalDate())
                .setStartDateTime(LocalDateTime.of(query.getDateTime().toLocalDate(), LocalTime.MIN))
                .setEndDateTime(query.getDateTime())
                .setStartMonthTime(day>28? dateTime : dateTime.plusMonths(-1)) // 上个月的28至本月28属于一个月
                .setEndMonthTime(query.getDateTime())
                .setStartYearTime(query.getDateTime().with(TemporalAdjusters.firstDayOfYear()).withHour(0).withMinute(0).withSecond(0))
                .setEndYearTime(query.getDateTime())
        ;
        Duration duration = Duration.between(query.getStartDateTime(),query.getEndDateTime());
        query.setHour(duration.toHours());
    }

    @Override
    public List<DataResultVO> queryDataResultList(DailyRecordListQuery query) {
        setQueryData(query);
        setQueryNightTime(query);
        List<DataResultVO> dataResult = baseMapper.getDataResult(query);
        // 装入西江的获取数据
        var xjDataResult = xjFlowDataService.getDataResult(dataResult.stream().filter(recordVO -> recordVO.getType().equals(Constant.EquipmentType.XJ.getCode())).collect(Collectors.toList()), query);
        // 丹灶的数据
        var dzDataResult = dzFlowDataService.getDataResult(dataResult.stream().filter(recordVO -> recordVO.getType().equals(Constant.EquipmentType.DZ.getCode())).collect(Collectors.toList()), query);
        // 手抄的数据
        var handDataResult = copyFlowDataService.getDataResult(dataResult.stream().filter(recordVO -> recordVO.getType().equals(Constant.EquipmentType.HAND.getCode())).collect(Collectors.toList()), query);
        var collect = dataResult.stream().filter(dataResultVO -> dataResultVO.getType().equals(Constant.EquipmentType.SPIDER.getCode())).collect(Collectors.toList());
        collect.addAll(xjDataResult);
        collect.addAll(dzDataResult);
        collect.addAll(handDataResult);
        return collect.stream().sorted(Comparator.comparing(DataResultVO::getId)).collect(Collectors.toList());
    }

    // 设置夜间时间
    private void setQueryNightTime(DailyRecordListQuery query) {
        LocalDateTime startNightTime = LocalDateTime.of(query.getDateTime().toLocalDate(), LocalTime.of(2, 0));
        LocalDateTime endNightTime = LocalDateTime.of(query.getDateTime().toLocalDate(), LocalTime.of(4, 0));
        if (query.getDateTime().isBefore(startNightTime)) {
            query.setStartNightTime(query.getDateTime()).setEndNightTime(query.getDateTime());// 当查询时间在夜间之前,用同个时间，让数值为0
        } else if (query.getDateTime().isBefore(endNightTime)) {
            query.setStartNightTime(startNightTime).setEndNightTime(query.getDateTime());// 当查询时间在夜间之间
        } else {
            query.setStartNightTime(startNightTime).setEndNightTime(endNightTime);
        }
    }

    private void setUndoDataByTime(LocalDateTime dateTime, ArrayList<UndoCopyDataVO> undoCopyDataVOS) {
        List<Integer> undoIds = copyFlowDataDao.getUndoDataIdsByDateTime(dateTime);
        if (CollectionUtils.isEmpty(undoIds)) {
            return;
        }
        UndoCopyDataVO undoVO = new UndoCopyDataVO().setIds(undoIds).setDateTime(dateTime);
        undoCopyDataVOS.add(undoVO);
    }

    @Override
    public void getElectricTask() {
        ElectricitySpiderCrawler crawler = new ElectricitySpiderCrawler();
        crawler.addSeed(diaoduUtils.getElectricUrl());
        try {
            crawler.start(4);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void getPressureFlowTask() {

        List<TbEquipmentEntity> equipmentList = this
                .list(new QueryWrapper<TbEquipmentEntity>()
                        .eq("type", Constant.EquipmentType.SPIDER.getCode())
                        .eq("spider_status", Constant.EquipmentSpiderStatus.DONE.getCode())
                        .eq("is_used", Constant.ONE)
                );
        equipmentList.forEach(equipment -> {
            if (StringUtils.isNotBlank(equipment.getTag())) {
                PressureSpiderCrawler pressureCrawler = new PressureSpiderCrawler();
                String pressureMaxTime = tbPressureStateDao.getMaxTimeByEquipmentId(equipment.getId());
                pressureMaxTime = checkIsNull(pressureMaxTime);
                var spiderTime = checkIsEndDay(pressureMaxTime);
                pressureCrawler.addSeed(diaoduUtils.getUrl() + "?tagname=" + equipment.getTag()
                        + "&year=" + spiderTime.substring(0, 4)
                        + "&month=" + spiderTime.substring(5, 7)
                        + "&mday=" + spiderTime.substring(8, 10));
                pressureCrawler.setIdAndMaxtime(equipment.getId(), pressureMaxTime);
                try {
                    pressureCrawler.start(4);
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            }
            if (StringUtils.isNotBlank(equipment.getFlowTag())) {
                FlowSpiderCrawler flowCrawler = new FlowSpiderCrawler();
                String flowMaxTime = tbFlowStateDao.getMaxTimeByEquipmentId(equipment.getId());
                flowMaxTime = checkIsNull(flowMaxTime);
                var spiderTime = checkIsEndDay(flowMaxTime);
                flowCrawler.addSeed(diaoduUtils.getUrl() + "?tagname=" + equipment.getFlowTag()
                        + "&year=" + spiderTime.substring(0, 4)
                        + "&month=" + spiderTime.substring(5, 7)
                        + "&mday=" + spiderTime.substring(8, 10));
                flowCrawler.setIdAndMaxtime(equipment.getId(), flowMaxTime);
                try {
                    flowCrawler.start(4);
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            }
        });
    }

    @Override
    public TrendVO queryTrendVO(DateInfoQuery query) {
        TbEquipmentEntity equipmentEntity = this.getById(query.getId());
        query.setStartDate(query.getDate().minusDays(30));// 设置起始为30日前
        TrendVO vo = new TrendVO();
        BeanUtils.copyProperties(equipmentEntity, vo);
        switch (equipmentEntity.getType()) { // 0手抄，1调度中心爬取，2丹灶数据，3西江数据
            case 0 :
                QueryWrapper<TbCopyFlowDataEntity> wrapper = new QueryWrapper<TbCopyFlowDataEntity>()
                        .select("DATE( date ) time", "MAX(positive_flow - negative_flow) - MIN(positive_flow - negative_flow) flowSum")
                        .eq("area_id", query.getId())
                        .le("date", query.getDate())
                        .ge("date", query.getStartDate())
                        .groupBy("DATE( date )")
                        .orderByAsc("date");
                vo.setList(copyFlowDataService.listMaps(wrapper));
                vo.setNightList(copyFlowDataService.listMaps(
                        wrapper
                        .le("HOUR(date)", 4)
                        .ge("HOUR(date)", 2)
                ));
                break;
            case 1 :
                QueryWrapper<TbFlowStateEntity> queryWrapper = new QueryWrapper<TbFlowStateEntity>()
                        .select("DATE( time ) time", "ROUND( SUM( `value` ) / 20, 3 ) flowSum")
                        .eq("equipment_id", query.getId())
                        .le("time", query.getDate())
                        .ge("time", query.getStartDate())
                        .groupBy("DATE( time )")
                        .orderByAsc("time");
                vo.setList(tbFlowStateService.listMaps(queryWrapper));
                vo.setNightList(tbFlowStateService.listMaps(queryWrapper
                        .le("HOUR(time)", 4)
                        .ge("HOUR(time)", 2)
                ));
                break;
            case 2 :
                dzFlowDataService.getTrendVo(vo, query);
                break;
            case 3 :
                xjFlowDataService.getTrendVo(vo, query);
                break;
            default :
                break;
        }

        return vo;
    }

    @Override
    public DetailVO queryDetailVO(DateInfoQuery query) {
        TbEquipmentEntity equipmentEntity = this.getById(query.getId());
        query.setStartDate(query.getDate().minusDays(30));// 设置起始为30日前
        DetailVO vo = new DetailVO();
        BeanUtils.copyProperties(equipmentEntity, vo);
        QueryWrapper<TbCopyFlowDataEntity> handWrapper = new QueryWrapper<TbCopyFlowDataEntity>()
                .select("date time", "positive_flow positiveFlow", "negative_flow negativeFlow", "instant_flow instantFlow")
                .eq("area_id", query.getId())
                .eq("DATE( date )", query.getDate())
                .orderByAsc("date");
        List<Map<String, Object>> handList = copyFlowDataService.listMaps(handWrapper);
        vo.setHandList(handList);
        switch (equipmentEntity.getType()) { // 0手抄，1调度中心爬取，2丹灶数据，3西江数据
            case 0 :
                vo.setHandList(handList);
                break;
            case 1 :
                QueryWrapper<TbFlowStateEntity> queryWrapper = new QueryWrapper<TbFlowStateEntity>()
                        .select("time", "value")
                        .eq("equipment_id", query.getId())
                        .eq("DATE( time )", query.getDate())
                        .orderByAsc("time");
                vo.setList(tbFlowStateService.listMaps(queryWrapper));
                break;
            case 2 :
                dzFlowDataService.getDetailVo(vo, query);
                break;
            case 3 :
                xjFlowDataService.getDetailVo(vo, query);
                break;
            default :
                break;
        }

        return vo;
    }

    @Override
    public void completionData(CompletionData data) {
        TbEquipmentEntity equipment = this.getById(data.getId());
        List<String> dateList = DateUtils.getBetweenDate(data.getStart(), data.getEnd());

        if (data.getType() == 1) {
            FlowSpiderCrawler flowCrawler = new FlowSpiderCrawler();
            dateList.forEach(date -> {
                flowCrawler.addSeed(diaoduUtils.getUrl() + "?tagname=" + equipment.getFlowTag()
                        + "&year=" + date.substring(0, 4)
                        + "&month=" + date.substring(5, 7)
                        + "&mday=" + date.substring(8, 10));
                flowCrawler.setIdAndMaxtime(equipment.getId(), date);
                try {
                    flowCrawler.start(4);
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            });

        } else if (data.getType() == 2) {
            PressureSpiderCrawler pressureCrawler = new PressureSpiderCrawler();
            dateList.forEach(date -> {
                pressureCrawler.addSeed(diaoduUtils.getUrl() + "?tagname=" + equipment.getTag()
                        + "&year=" + date.substring(0, 4)
                        + "&month=" + date.substring(5, 7)
                        + "&mday=" + date.substring(8, 10));
                pressureCrawler.setIdAndMaxtime(equipment.getId(), date);
                try {
                    pressureCrawler.start(4);
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            });
        }
    }

    private String checkIsNull(String maxTime) {
        if (StringUtils.isBlank(maxTime)) {
            maxTime = diaoduUtils.getBeginTime();
        }
        return maxTime;
    }

    /**
     * 如果是23：57的时间，需要设置为新的一天
     * @param maxTime
     * @return
     */
    private String checkIsEndDay(String maxTime) {
        if (maxTime.startsWith("23", 11) && maxTime.startsWith("57", 14)) {
            return LocalDateUtils.dateFormat(LocalDateTime.of(
                    LocalDateTime.parse(maxTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).plusDays(1).toLocalDate()
                    , LocalTime.MIN), LocalDateUtils.DATE_TIME_PATTERN)
                    ;
        }
        return maxTime;
    }

    // 校验压力和流量是否更新到今天日期
    private void checkIsSpiderSuccess(Integer id) {
        String pressureMaxTime = tbPressureStateDao.getMaxTimeByEquipmentId(id);
        String flowMaxTime = tbFlowStateDao.getMaxTimeByEquipmentId(id);
        String nowDate = DateUtils.format(new Date());
        if (StringUtils.isNotBlank(pressureMaxTime) && StringUtils.isNotBlank(flowMaxTime)) {
            boolean isAllSuccess = pressureMaxTime.split(" ")[0].equals(nowDate) &&
                    flowMaxTime.split(" ")[0].equals(nowDate);
            if (isAllSuccess) {
                this.lambdaUpdate().set(TbEquipmentEntity::getSpiderStatus, Constant.EquipmentSpiderStatus.DONE.getCode())
                        .eq(TbEquipmentEntity::getId, id)
                        .update();
            }
        }
    }
}